package com.har01d.lang.compiler.visitor.statement;

import com.har01d.lang.antlr.Har01dBaseVisitor;
import com.har01d.lang.antlr.Har01dParser.AssignmentContext;
import com.har01d.lang.compiler.domain.Scope;
import com.har01d.lang.compiler.domain.function.FunctionReference;
import com.har01d.lang.compiler.domain.function.FunctionSignature;
import com.har01d.lang.compiler.domain.statement.Assignment;
import com.har01d.lang.compiler.domain.statement.expression.Expression;
import com.har01d.lang.compiler.domain.type.FunctionType;
import com.har01d.lang.compiler.domain.variable.LocalVariable;
import com.har01d.lang.compiler.exception.InvalidSyntaxException;
import com.har01d.lang.compiler.visitor.statement.expression.ExpressionVisitor;

public class AssignmentStatementVisitor extends Har01dBaseVisitor<Assignment> {

    private final ExpressionVisitor expressionVisitor;
    private final Scope scope;

    public AssignmentStatementVisitor(ExpressionVisitor expressionVisitor, Scope scope) {
        this.expressionVisitor = expressionVisitor;
        this.scope = scope;
    }

    @Override
    public Assignment visitAssignment(AssignmentContext ctx) {
        String name = ctx.name().getText();
        LocalVariable variable = scope.getVariable(name);
        if (variable == null) {
            variable = scope.getField(name);
        }

        if (variable == null) {
            throw new InvalidSyntaxException("Variable '" + name + "' doesn't declared!", ctx);
        }

        if (variable.isInitialized() && variable.isReadonly()) {
            throw new InvalidSyntaxException("Variable '" + name + "' is readonly!", ctx);
        }

        variable.setInitialized(true);
        Expression expression = ctx.expression().accept(expressionVisitor);

        if (variable.getType() instanceof FunctionType) {
            if (expression instanceof FunctionReference) {
                FunctionSignature signature = ((FunctionReference) expression).getFunctionSignature();
                scope.addSignature(new FunctionSignature(name, signature));
            }
            expression = null;
        }

        // TODO: check type
        return new Assignment(variable, expression);
    }

}
